/**
 * @(#) NXTSensor.java
 */

package pcsr.drivers.nxtDriver;

import pcsr.drivers.nxtDriver.command.*;

abstract class NXTSensor implements INXTSensor
{
	/**
	 * The default I2C address for a port. 
	 * You can change address of compass sensor (see docs) 
	 * and then communicate with multiple sensors on same physical port.
	 */
	protected static byte I2C_DEFAULT_ADDRESS = 0x02;
	
	private INXTCommand btCommandManager;
	
	private NXTSensorMode mode;
	
	private NXTPort port;

	private NXTSensorType type;
	
	private double value;
	
	public NXTSensor( NXTPort sensor, INXTCommand command ) {
		SetPort(sensor);
		btCommandManager = command;
	}

	public abstract NXTSensorMode[] GetAvailableModes( );
	
	@Override
	public NXTSensorMode GetMode( ) {		
		return mode;
	}

	@Override
	public NXTSensorType GetType( ) {
		return type;
	}

	@Override
	public void SetMode( NXTSensorMode newmode ) {
		mode = newmode;
	}

	@Override
	public void SetType( NXTSensorType newtype ) {
		type = newtype;
	}
	
	/**
	 * Method for retrieving data values from the sensor. BYTE0 (icommand.nxtcomm.I2CProtocol)
	 * is usually the primary data value for the sensor.
	 * Data is read from registers in the sensor, usually starting at 0x00 and ending around 0x49.
	 * Just supply the register to start reading at, and the length of bytes to read (16 maximum).
	 * NOTE: The NXT supplies UBYTE (unsigned byte) values but Java converts them into
	 * signed bytes (probably more practical to return short/int?)
	 * @param register e.g. FACTORY_SCALE_DIVISOR, BYTE0, etc....
	 * @param length Length of data to read (minimum 1, maximum 16)
	 * @return data received
	 */
	protected synchronized byte[] getData( byte register, int length ) {
		byte [] txData = {I2C_DEFAULT_ADDRESS, register};
		btCommandManager.LSWrite((byte)GetPort().GetPortID(), txData, (byte)length);
		
		byte [] status;
		do {
			status = btCommandManager.LSGetStatus((byte)GetPort().GetPortID());
		} while(status[0] == ErrorMessages.PENDING_COMMUNICATION_TRANSACTION_IN_PROGRESS|status[0] == ErrorMessages.SPECIFIED_CHANNEL_CONNECTION_NOT_CONFIGURED_OR_BUSY);
		
		if(status[1] == 0) {
			System.out.println("No bytes to be read in I2CSensor.getData(). Returning 0.");
			return new byte[1];
		}
				
		byte [] result = btCommandManager.LSRead((byte)GetPort().GetPortID());
		return result;
	}

	/**
	 * Reads the boolean value of the sensor.
	 * @return Boolean value of sensor.
	 */
	protected synchronized boolean readBooleanValue( ) {
		InputValues vals = btCommandManager.GetInputValues(GetPort().GetPortID());		
		return vals.rawADValue< 500;
	}

	/**
	 * Reads the normalized value of the sensor.
	 * @return Normalized value. 0 to 1023
	 */
	protected synchronized int readNormalizedValue( ) {
		InputValues vals = btCommandManager.GetInputValues(GetPort().GetPortID());
		return vals.normalizedADValue;
	}

	/**
	 * Reads the raw value of the sensor.
	 * @return Raw sensor value. Range is device dependent.
	 */
	protected synchronized int readRawValue( ) {
		InputValues vals = btCommandManager.GetInputValues(GetPort().GetPortID());
		return vals.rawADValue;
	}

	/**
	 * Returns scaled value, depending on mode of sensor.
	 * e.g. BOOLEANMODE returns 0 or 1.
	 * e.g. PCTFULLSCALE returns 0 to 100.
	 * @return the value of the sensor
	 * @see NXTPort
	 */
	protected synchronized int readScaledValue( ) {
		InputValues vals = btCommandManager.GetInputValues(GetPort().GetPortID());
		return vals.scaledValue;
	}

	/**
	 * Sets a single byte in the I2C sensor.
	 * @param register A data register in the I2C sensor. e.g. ACTUAL_ZERO
	 * @param value The data value.
	 */
	protected void sendData( byte register, byte value ) {
		byte [] txData = {I2C_DEFAULT_ADDRESS, register, value};
		btCommandManager.LSWrite((byte)GetPort().GetPortID(), txData, (byte)0);
	}
	
	/**
	 * Sets the type of sensor and mode of operation.
	 */
	protected synchronized void setTypeAndMode( NXTSensorType newtype, NXTSensorMode newmode ) {
		btCommandManager.SetInputMode(GetPort().GetPortID(), newtype.GetTypeValue(), newmode.GetModeValue());
		mode = newmode;
		type = newtype;
	}

	/**
	 * Sets the type of sensor and mode of operation.
	 */
	protected synchronized void SetSensorState( ) {
		btCommandManager.SetInputMode(GetPort().GetPortID(), GetType().GetTypeValue(), GetMode().GetModeValue());
	}
	/**
	 * @param port the port to set
	 */
	void SetPort( NXTPort port ) {
		this.port = port;
	}

	/**
	 * @return the port
	 */
	public NXTPort GetPort( ) {
		return port;
	}

	/**
	 * @param value the value to set
	 */
	public void SetSensorValue( double value ) {
		this.value = value;
	}

	/**
	 * @return the value
	 */
	public double GetSensorValue( ) {
		return value;
	}
	
	abstract int UpdateActuatorState( );
	
}
